// const ages = [1, 2, 3];
// Reflect.apply(fn, thisArg, args) 方法等同于 Function.prototype.apply.call(fn, obj, args) 用于绑定this对象后执行给定函数
// Function.prototype.apply.call(fn, obj, args) 简版 func.apply(thisArg, args)
// 简版潜在问题，因为func可能是一个已有自定apply的对象
// 例如：Math.max.apply(Math, ages) 等同于 Reflect.apply(Math.max, Math, ages);
/**
 * Reflect.apply() 则要求所有参数都必传
 *
 */

// 对象字面量创建对象与 Object.create(null)创建对象有什么区别
/**
 * 对象字面量创建对象, 展开后还会有原型链对象(__proto__)继承了Object的方法和属性
 *
 * Object.create(null)创建对象，展开是没有
 */

// 有一个二维数组，我想让他变成一维数组：
// const arr = [1, 2, 3, [4, 5, 6]]
// console.log(arr.flat()) // [ 1, 2, 3, 4, 5, 6 ]
// 传参数，参数为降维的次数 console.log(arr.flat(2))
// arr.flat(Infinity)

// Object.entries是把对象转成键值对数组，而Object.fromEntries则相反，是把键值对数组转为对象
// const arr = [
//   ['name', '林三心'],
//   ['age', 22],
//   ['gender', '男']
// ]
//
// console.log(Object.fromEntries(arr)) // { name: '林三心', age: 22, gender: '男' }

// 先说说?.，中文名为可选链
// 1. 比如我们需要一个变量，是数组且有长度，才做某些操作
// const list = null
// if (list && list.length) {
//   // do something
// }
//
// // 使用可选链
// if (list?.length) {
//   // do something
// }

// 2. 一个对象，我要取一个可能不存在的值，甚至我们都不确定obj是否存在
// const obj = {
//   cat: {
//     name: '哈哈'
//   }
// }
// const dog = obj && obj.dog && obj.dog.name // undefined
// // 可选链
// const dog2 = obj?.dog?.name // undefined

// 3. 比如有一个数组，我不确定它存不存在，存在的话就取索引为1的值
// const arr = null
// const item = arr && arr[1]
// // 可选链
// const item2 = arr?.[1]

// 4.比如有一个函数，我们不确定它存不存在，存在的话就执行它
// const fn = null
// const res = fn && fn()
// // 可选链
// const res2 = fn?.()

// 再说说??，中文名为空位合并运算符
// const a = 0 || '林三心' // 林三心
// const b = '' || '林三心' // 林三心
// const c = false || '林三心' // 林三心
// const d = undefined || '林三心' // 林三心
// const e = null || '林三心' // 林三心

// // 而??和||最大的区别是，在??这，只有undefined和null才算假值
// const a = 0 ?? '林三心' // 0
// const b = '' ?? '林三心' // ''
// const c = false ?? '林三心' // false
// const d = undefined ?? '林三心' // 林三心
// const e = null ?? '林三心' // 林三心

// 数组分割
// function Chunk(arr = [], size = 1) {
//   return arr.length
//     ? arr.reduce((t, v) => (t[t.length - 1].length === size ? t.push([v]) : t[t.length - 1].push(v), t), [[]])
//     : [];
// }

// 数组去重
// function Uniq(arr = []) {
//   return arr.reduce((t, v) => t.includes(v) ? t : [...t, v], []);
// }

// 数组最大最小值
// function Max(arr = []) {
//   return arr.reduce((t, v) => t > v ? t : v);
// }
//
// function Min(arr = []) {
//   return arr.reduce((t, v) => t < v ? t : v);
// }

// 统计元素出现个数
// function Count(arr = []) {
//   return arr.reduce((t, v) => ((t[v] = (t[v] || 0) + 1), t), {});
// }
// const nameArr = ['林三心', 'sunshine_lin', '林三心', '林三心', '科比'];
// const totalObj = nameArr.reduce((pre, next) => {
//   if (pre[next]) {
//     pre[next]++;
//   } else {
//     pre[next] = 1;
//   }
//   return pre;
// }, {});

// ES2020 引入了“链判断运算符”（optional chaining operator）?.，简化上面的写法。
// const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default';
// const firstName = message?.body?.user?.firstName || 'default';

// const headerText = response.settings.headerText || 'Hello, world!';
// const animationDuration = response.settings.animationDuration || 300;
// const showSplashScreen = response.settings.showSplashScreen || true;
// 上面的三行代码都通过||运算符指定默认值，但是这样写是错的。开发者的原意是，只要属性的值为null或undefined，默认值就会生效，但是属性的值如果为空字符串或false或0，默认值也会生效
// ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||，但是只有运算符左侧的值为null或undefined时，才会返回右侧的值。
// const headerText = response.settings.headerText ?? 'Hello, world!';
// const animationDuration = response.settings.animationDuration ?? 300;
// const showSplashScreen = response.settings.showSplashScreen ?? true;
// const animationDuration = response.settings?.animationDuration ?? 300;

// 方式1：借助promise.prototype.finally包
// npm install promise-prototype-finally

// 方式2：手动实现finally()
Promise.prototype.finally = function(callback) {
  let P = this.constructor;
  return this.then(
    value => P.resolve(callback()).then(() => value),
    reason =>
      P.resolve(callback()).then(() => {
        throw reason;
      })
  );
};
